package org.example.batch;

import ai.grazie.utils.json.JSONObject;
import com.fasterxml.jackson.jr.ob.JSON;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogBuilder;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.SmartPointerManager;
import com.intellij.psi.SmartPsiElementPointer;
import com.intellij.psi.search.FileTypeIndex;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.rt.debugger.coroutines.JsonUtils;
import nonapi.io.github.classgraph.json.JSONUtils;
import org.example.demo.DataSourceConfig;
import org.example.demo.SqlExecutor;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import java.awt.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;
import java.util.List;
import static org.apache.commons.lang3.StringEscapeUtils.escapeJson;
import static org.example.demo.SqlValidator.formatSQL;

public class BatchCheckAllXmlFilesAction extends AnAction {

    private static final Logger log = LoggerFactory.getLogger(BatchCheckAllXmlFilesAction.class);

    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        Project project = e.getProject();
        if (project == null || project.isDisposed()) return;

        new Task.Backgroundable(project, "批量检查所有 XML 中的 SQL", true) {
            @Override
            public void run(@NotNull ProgressIndicator indicator) {
                Map<String, List<SqlCheckResult>> allResults = ReadAction.compute(() -> {
                    if (project.isDisposed()) {
                        return Collections.emptyMap();
                    }

                    Collection<VirtualFile> xmlFiles = FileTypeIndex.getFiles(
                            com.intellij.ide.highlighter.XmlFileType.INSTANCE,
                            GlobalSearchScope.projectScope(project)
                    );
                    VirtualFile targetFile = e.getData(CommonDataKeys.VIRTUAL_FILE);
                    boolean isDirectory = targetFile != null && targetFile.isDirectory();
                    boolean isXmlFile = targetFile != null && !targetFile.isDirectory() && "xml".equalsIgnoreCase(targetFile.getExtension());
                    Collection<VirtualFile> xmlFilesToCheck = new ArrayList<>();

                    if (isXmlFile) {
                        // 1. 右键点击 XML 文件：只检查该文件
                        xmlFilesToCheck.add(targetFile);
                    } else if (isDirectory) {
                        // 2. 右键点击目录：只检查本目录下的 XML（不递归子目录）
                        VirtualFile[] children = targetFile.getChildren();
                        if (children != null) {
                            for (VirtualFile child : children) {
                                if (child != null && !child.isDirectory() && "xml".equalsIgnoreCase(child.getExtension())) {
                                    xmlFilesToCheck.add(child);
                                }
                            }
                        }
                    } else {
                        // 3. 其他情况：扫描整个项目（可选，如不需要可 return emptyMap）
                        xmlFilesToCheck = FileTypeIndex.getFiles(
                                com.intellij.ide.highlighter.XmlFileType.INSTANCE,
                                GlobalSearchScope.projectScope(project)
                        );
                    }

                    Map<String, List<SqlCheckResult>> results = new LinkedHashMap<>();


                    int total = xmlFiles.size();
                    int i = 0;
                    for (VirtualFile vf : xmlFilesToCheck) {
                        if (indicator.isCanceled()) break;
                        indicator.setFraction((double) ++i / total);
                        String path = vf.getPath();
                        String displayPath = path.length() > 60 ? "..." + path.substring(path.length() - 57) : path;
                        indicator.setText("检查: " + displayPath);

                        XmlFile xmlFile = (XmlFile) PsiManager.getInstance(project).findFile(vf);
                        if (xmlFile == null) continue;

                        XmlTag root = xmlFile.getRootTag();
                        if (root == null) continue;

                        List<SqlCheckResult> fileResults = new ArrayList<>();
                        checkTagRecursive(root, fileResults, project);
                        if (!fileResults.isEmpty()) {
                            results.put(vf.getPath(), fileResults);
                        }
                    }

                    return results;
                });

                ApplicationManager.getApplication().invokeLater(() -> {
                    if (!project.isDisposed()) {
                        showSummaryDialog(project, allResults);
                    }
                }, project.getDisposed());
            }
        }.queue();
    }

    private void checkTagRecursive(XmlTag tag, List<SqlCheckResult> results, Project project) {
        String tagName = tag.getName();
        if ("select".equalsIgnoreCase(tagName) ||
            "update".equalsIgnoreCase(tagName) ||
            "insert".equalsIgnoreCase(tagName) ||
            "delete".equalsIgnoreCase(tagName)) {

            String rawSql = tag.getValue().getTrimmedText();
            if (rawSql.isEmpty()) {
                // 可选：记录空 SQL，但这里我们跳过
                return;
            }

            // 格式化并验证 SQL
            String formattedSql = formatSQL(rawSql);
            if (formattedSql == null || "EMPTY".equals(formattedSql)) {
                // 验证失败，记录错误
                SqlExecutor.Result errorResult = new SqlExecutor.Result();
                errorResult.success = false;
                errorResult.message = "SQL 格式无效或为空";
                results.add(new SqlCheckResult(tagName, rawSql, errorResult, tag, project));
                return;
            }

            // 替换参数
            String parsedSql = formattedSql.replaceAll("\\#\\{[^}]+\\}", "'-1'").trim();

            // 执行 SQL
            SqlExecutor.Result execResult = SqlExecutor.execute(project, parsedSql);
            results.add(new SqlCheckResult(tagName, rawSql, execResult, tag, project));
        }

        for (XmlTag child : tag.getSubTags()) {
            checkTagRecursive(child, results, project);
        }
    }

    private void showSummaryDialog(Project project, Map<String, List<SqlCheckResult>> allResults) {
        // 重新统计：遍历所有文件中的所有结果（只包含失败项）
        int totalChecked = 0;
        int invalid = 0;

        // 我们需要知道总共扫描了多少条 SQL（包括成功的），但当前 allResults 只含失败项
        // 所以我们无法准确知道 totalChecked。因此，我们修改策略：
        // → 在 checkTagRecursive 中收集 total count，但为简化，这里仅统计失败数

        // 实际上，为了准确统计，最好在后台任务中同时返回 totalSqlCount
        // 但为最小改动，我们只显示错误数

        for (List<SqlCheckResult> list : allResults.values()) {
            invalid += list.size();
        }

        StringBuilder summary = new StringBuilder();
        summary.append("📊 扫描完成！\n");
        summary.append("📁 共检查 ").append(allResults.size()).append(" 个 XML 文件\n");
        summary.append("❌ 错误 SQL: ").append(invalid).append("\n");

        if (invalid == 0) {
            summary.append("✅ 所有 SQL 均通过检查！");
            Messages.showMessageDialog(project, summary.toString(), "批量 SQL 检查结果", Messages.getInformationIcon());
            return;
        }

        // 只显示错误项
        DefaultListModel<String> listModel = new DefaultListModel<>();
        List<SqlCheckResult> errorList = new ArrayList<>();

        for (Map.Entry<String, List<SqlCheckResult>> entry : allResults.entrySet()) {
            String filePath = entry.getKey();
            for (SqlCheckResult r : entry.getValue()) {
                StringBuilder msg = getBuilder(r, filePath);
                listModel.addElement(msg.toString());
                errorList.add(r);
            }
        }

        JList<String> errorJList = new JList<>(listModel);
        errorJList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        errorJList.setCellRenderer(new DefaultListCellRenderer() {
            @Override
            public Component getListCellRendererComponent(JList<?> list, Object value, int index,
                                                          boolean isSelected, boolean cellHasFocus) {
                super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                setToolTipText(value.toString());
                return this;
            }
        });

        JPopupMenu popupMenu = new JPopupMenu();
        JMenuItem viewDetailItem = getDetailItem(project, errorJList, errorList);
        popupMenu.add(viewDetailItem);

        JMenuItem locateSqlItem = getSqlItem(project, errorJList, errorList);
        popupMenu.add(locateSqlItem);

        // 新增 AI 修复项
        popupMenu.add(getFixWithAIItem(project, errorJList, errorList));

        errorJList.setComponentPopupMenu(popupMenu);

        JScrollPane scrollPane = new JScrollPane(errorJList);
        scrollPane.setPreferredSize(new Dimension(700, 400));

        DialogBuilder builder = new DialogBuilder(project);
        builder.setTitle("批量 SQL 检查结果（仅显示错误）");

        JLabel summaryLabel = new JLabel("<html><pre>" + summary.toString().replace("\n", "<br>") + "</pre></html>");
        summaryLabel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

        JPanel topPanel = new JPanel(new BorderLayout());
        topPanel.add(summaryLabel, BorderLayout.NORTH);
        topPanel.add(scrollPane, BorderLayout.CENTER);

        builder.setCenterPanel(topPanel);
        builder.addOkAction();
        builder.show();
    }

    private static @NotNull JMenuItem getSqlItem(Project project, JList<String> errorJList, List<SqlCheckResult> errorList) {
        JMenuItem locateSqlItem = new JMenuItem("定位 SQL");
        locateSqlItem.addActionListener(e -> {
            int idx = errorJList.getSelectedIndex();
            if (idx >= 0 && idx < errorList.size()) {
                SqlCheckResult result = errorList.get(idx);
                ApplicationManager.getApplication().runReadAction(() -> {
                    XmlTag tag = result.tagPointer.getElement();
                    if (tag != null && tag.isValid() && !project.isDisposed()) {
                        ApplicationManager.getApplication().invokeLater(() -> {
                            if (!project.isDisposed()) {
                                new OpenFileDescriptor(
                                        project,
                                        tag.getContainingFile().getVirtualFile(),
                                        tag.getTextOffset()
                                ).navigate(true);
                            }
                        }, project.getDisposed());
                    }
                });
            }
        });
        return locateSqlItem;
    }

    private static @NotNull JMenuItem getDetailItem(Project project, JList<String> errorJList, List<SqlCheckResult> errorList) {
        JMenuItem viewDetailItem = new JMenuItem("查看执行详情");
        viewDetailItem.addActionListener(e -> {
            int idx = errorJList.getSelectedIndex();
            if (idx >= 0 && idx < errorList.size()) {
                SqlCheckResult result = errorList.get(idx);
                ApplicationManager.getApplication().runReadAction(() -> {
                    XmlTag tag = result.tagPointer.getElement();
                    if (tag != null) {
                        ApplicationManager.getApplication().invokeLater(() -> {
                                Messages.showMessageDialog(
                                        project,
                                        errorJList.getModel().getElementAt(idx),
                                        "SQL 执行详情",
                                        Messages.getInformationIcon()
                                );
                        }, project.getDisposed());
                    }
                });
            }
        });
        return viewDetailItem;
    }


    private static @NotNull JMenuItem getFixWithAIItem(Project project, JList<String> errorJList, List<SqlCheckResult> errorList) {
        JMenuItem aiFixItem = new JMenuItem("AI 自动修复（通义灵码）");
        aiFixItem.addActionListener(e -> {
            int idx = errorJList.getSelectedIndex();
            if (idx < 0 || idx >= errorList.size()) {
                Messages.showMessageDialog(project, "请先选择一个错误项", "提示", Messages.getWarningIcon());
                return;
            }
            DataSourceConfig config = DataSourceConfig.getInstance(project);
            if (config.aiKey.isEmpty()){
                Messages.showMessageDialog(project, "缺少同义请求私钥key请在工具栏数据源配置控制面板进行配置", "提示", Messages.getInformationIcon());
                return;
            }

            SqlCheckResult result = errorList.get(idx);
            if (result.result.success){
                Messages.showMessageDialog(project, "语法正确无需修复", "提示", Messages.getInformationIcon());
                return;
            }
            String errorMessage =result.result.message;
            String sqlSnippet = result.sql; // 你需要实现这个方法

            if (sqlSnippet == null || sqlSnippet.trim().isEmpty()) {
                Messages.showMessageDialog(project, "无法提取 SQL 片段", "错误", Messages.getErrorIcon());
                return;
            }

            // 构造 prompt
            String prompt = String.format(
                    "你是一个 "+config.dbType+" SQL 语法 专家 。以下 SQL 语句存在错误：%n%n%s%n%n错误信息：%s%n%n请给出修复后的正确 SQL 语句，，不要解释。",
                    sqlSnippet, errorMessage
            );

         //   ApplicationManager.getApplication().executeOnPooledThread(() -> {
                if (project.isDisposed()) return;

                // 1. 先在 ReadAction 中安全读取 SQL（如果你还没读）
                // （假设 prompt 已经在外部正确构造好）

                String fixedSql = null;
                try {
                    fixedSql = callQwenAPI(prompt,config); // ✅ 在后台线程调用网络，不阻塞 UI
                } catch (Exception ex) {
                    // 网络异常，切回 UI 线程提示
                    ApplicationManager.getApplication().invokeLater(() -> {
                        if (!project.isDisposed()) {
                            Messages.showMessageDialog(project, "调用 AI 服务失败: " + ex.getMessage(), "错误", Messages.getErrorIcon());
                        }
                    }, project.getDisposed());
                    return;
                }

                // 2. 切回 EDT 显示结果
                String finalFixedSql = fixedSql;
                ApplicationManager.getApplication().invokeLater(() -> {
                    if (project.isDisposed()) return;

                    if (finalFixedSql != null && !finalFixedSql.trim().isEmpty()) {


                        int choice = Messages.showYesNoDialog(
                                project,
                                "AI 建议修复如下：\n\n" + finalFixedSql + "\n\n是否应用此修复？",
                                "AI 自动修复建议",
                                Messages.getQuestionIcon()
                        );
                        if (choice == Messages.YES) {
                            // 写操作必须在 WriteCommandAction 中
                            WriteCommandAction.runWriteCommandAction(project, () -> {
                                XmlTag tag = result.tagPointer.getElement();
                                if (tag != null && !project.isDisposed()) {
                                    tag.getValue().setText(finalFixedSql);
                                }
                            });
                            Messages.showMessageDialog(project, "修复已应用！", "成功", Messages.getInformationIcon());
                        }
                    } else {
                        Messages.showMessageDialog(project, "AI 未能生成有效修复建议", "失败", Messages.getErrorIcon());
                    }
                }, project.getDisposed());
           // });



        });
        return aiFixItem;
    }

    private static String extractSqlFromXmlTag(XmlTag tag) {
        if (tag == null) return null;
        // 假设 SQL 内容在 <sql> 标签的文本中，或某个属性中
        return tag.getValue().getTrimmedText();
    }

    private static String callQwenAPI(String prompt, DataSourceConfig config) throws IOException {
        // 使用阿里云百炼或 DashScope 的 Qwen API
        // 示例：DashScope（通义实验室官方 API）
        String apiKey = config.aiKey;
        // 建议从插件设置中读取
        String url = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation";

        String jsonPayload = "{\n" +
                "  \"model\": \"qwen-max\",\n" +
                "  \"input\": {\n" +
                "    \"messages\": [\n" +
                "      {\"role\": \"system\", \"content\": \"" + escapeJson("你是一个 "+config.dbType+" SQL 语法 专家 ，请只返回修复后的 SQL 语句，不要任何解释、不要 markdown、不要引号，直接输出可执行的 SQL（例如：select * from xxx）") + "\"},\n" +
                "      {\"role\": \"user\", \"content\": \"" + escapeJson(prompt) + "\"}\n" +
                "    ]\n" +
                "  },\n" +
                "  \"parameters\": {\n" +
                "    \"max_tokens\": 1024,\n" +
                "    \"temperature\": 0.1,\n" +
                "    \"top_p\": 0.8\n" +
                "  }\n" +
                "}";

        HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Authorization", "Bearer " + apiKey);
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setDoOutput(true);

        try (OutputStream os = conn.getOutputStream()) {
            os.write(jsonPayload.getBytes("UTF-8"));
        }

        try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"))) {
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null) response.append(line);
            // 解析 JSON 响应，提取 content
            // 简化处理：你可以用 Gson / Jackson
            String responseStr = response.toString();
            JsonObject root = JsonParser.parseString(responseStr).getAsJsonObject();
            String text = root
                    .getAsJsonObject("output")
                    .get("text")
                    .getAsString();
             return text;
        }
    }


    private static @NotNull StringBuilder getBuilder(SqlCheckResult r, String filePath) {
        StringBuilder msg = new StringBuilder();
        msg.append("<<").append(r.type.toUpperCase()).append(" in ").append(shortPath(filePath)).append(">>\n");
        SqlExecutor.Result result = r.result;
        if (result.success) {
            msg.append("✅ 执行成功\n");
            msg.append("耗时: ").append(result.duration).append("ms\n\n");

            if (result.isQuery) {
                msg.append("【查询结果】\n");
                if (result.rows.isEmpty()) {
                    msg.append("(无数据)\n");
                } else {
                    msg.append(String.join(" | ", result.columns)).append("\n");
                    msg.append("-".repeat(Math.min(80, msg.length()))).append("\n");
                    for (Map<String, Object> row : result.rows) {
                        for (String col : result.columns) {
                            Object val = row.get(col);
                            msg.append(val == null ? "NULL" : val.toString()).append(" | ");
                        }
                        msg.delete(msg.length() - 3, msg.length()).append("\n");
                    }
                }
            } else {
                msg.append("类型: 更新/DDL\n");
                msg.append("影响行数: ").append(result.affectedRows).append("\n");
            }
        } else {
            // ❌ 执行失败，显示错误
            msg.append("❌ 执行失败！\n\n");
            msg.append("SQL: \n").append(r.sql).append("\n\n");
            msg.append("错误信息: \n").append(result.message != null ? result.message : "未知错误");
        }
        return msg;
    }

    private static String shortPath(String path) {
        if (path.length() <= 50) return path;
        int sep = path.lastIndexOf('/');
        if (sep == -1) sep = path.lastIndexOf('\\');
        if (sep > 20) {
            return "..." + path.substring(sep);
        }
        return path.substring(0, 20) + "..." + path.substring(path.length() - 20);
    }


    @Override
    public void update(@NotNull AnActionEvent e) {
        Project project = e.getProject();
        // 只要项目存在，就显示菜单项（即使没选中文件，也可以扫描整个项目）
        e.getPresentation().setEnabledAndVisible(project != null && !project.isDisposed());
    }
}